import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import threading
from PIL import Image
import glob
import struct
import numpy as np

class ImageConverter:
    def __init__(self, root):
        self.root = root
        self.root.title("图像格式转换器")
        self.root.geometry("600x400")
        self.root.resizable(True, True)
        
        # 设置样式
        style = ttk.Style()
        style.theme_use('clam')
        
        self.setup_ui()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        # 标题
        title_label = ttk.Label(main_frame, text="图像格式转换器", font=("Arial", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        # 输出格式选择
        ttk.Label(main_frame, text="输出格式:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.format_var = tk.StringVar(value="jpg")
        format_frame = ttk.Frame(main_frame)
        format_frame.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        
        ttk.Radiobutton(format_frame, text="JPG", variable=self.format_var, value="jpg").pack(side=tk.LEFT, padx=(0, 20))
        ttk.Radiobutton(format_frame, text="PNG", variable=self.format_var, value="png").pack(side=tk.LEFT)
        
        # 输入文件夹选择
        ttk.Label(main_frame, text="输入文件夹:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.input_path_var = tk.StringVar()
        input_entry = ttk.Entry(main_frame, textvariable=self.input_path_var, width=50)
        input_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 10), pady=5)
        ttk.Button(main_frame, text="浏览", command=self.select_input_folder).grid(row=2, column=2, pady=5)
        
        # 输出文件夹选择
        ttk.Label(main_frame, text="输出文件夹:").grid(row=3, column=0, sticky=tk.W, pady=5)
        self.output_path_var = tk.StringVar()
        output_entry = ttk.Entry(main_frame, textvariable=self.output_path_var, width=50)
        output_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 10), pady=5)
        ttk.Button(main_frame, text="浏览", command=self.select_output_folder).grid(row=3, column=2, pady=5)
        
        # 转换按钮
        self.convert_btn = ttk.Button(main_frame, text="开始转换", command=self.start_conversion)
        self.convert_btn.grid(row=4, column=0, columnspan=3, pady=20)
        
        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
        self.progress_bar.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        
        # 状态标签
        self.status_var = tk.StringVar(value="准备就绪")
        status_label = ttk.Label(main_frame, textvariable=self.status_var)
        status_label.grid(row=6, column=0, columnspan=3, pady=5)
        
        # 日志文本框
        log_frame = ttk.LabelFrame(main_frame, text="转换日志", padding="10")
        log_frame.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
        log_frame.columnconfigure(0, weight=1)
        log_frame.rowconfigure(0, weight=1)
        
        self.log_text = tk.Text(log_frame, height=8, width=70)
        scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=scrollbar.set)
        
        self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 配置主框架的行权重
        main_frame.rowconfigure(7, weight=1)
        
    def select_input_folder(self):
        folder = filedialog.askdirectory(title="选择输入文件夹")
        if folder:
            self.input_path_var.set(folder)
            
    def select_output_folder(self):
        folder = filedialog.askdirectory(title="选择输出文件夹")
        if folder:
            self.output_path_var.set(folder)
            
    def log_message(self, message):
        # 使用 after 方法确保在主线程中更新 GUI，添加小延迟确保更新
        self.root.after(10, self._update_log, message)
        
    def _update_log(self, message):
        try:
            self.log_text.insert(tk.END, message + "\n")
            self.log_text.see(tk.END)
            self.root.update_idletasks()
        except Exception as e:
            print(f"日志更新错误: {e}")
        
    def update_progress(self, progress):
        self.root.after(0, lambda: self.progress_var.set(progress))
        
    def update_status(self, status):
        self.root.after(0, lambda: self.status_var.set(status))
        
    def enable_convert_button(self):
        self.root.after(0, lambda: self.convert_btn.config(state='normal'))
        
    def read_spe_file(self, file_path):
        """读取SPE文件并转换为PIL Image对象"""
        try:
            with open(file_path, 'rb') as f:
                # 读取SPE文件头
                # SPE文件头通常是4100字节
                header = f.read(4100)
                
                # 读取图像尺寸信息
                f.seek(42)
                xdim = struct.unpack('<H', f.read(2))[0]
                f.seek(656)
                ydim = struct.unpack('<H', f.read(2))[0]
                
                # 读取数据类型
                f.seek(108)
                datatype = struct.unpack('<H', f.read(2))[0]
                
                # 根据数据类型确定字节数
                if datatype == 0:  # float32
                    bytes_per_pixel = 4
                elif datatype == 1:  # int32
                    bytes_per_pixel = 4
                elif datatype == 2:  # int16
                    bytes_per_pixel = 2
                elif datatype == 3:  # uint16
                    bytes_per_pixel = 2
                else:
                    bytes_per_pixel = 2  # 默认
                
                # 读取图像数据
                f.seek(4100)  # 跳过文件头
                data = f.read(xdim * ydim * bytes_per_pixel)
                
                # 根据数据类型解析数据
                if datatype == 0:  # float32
                    array = np.frombuffer(data, dtype=np.float32)
                elif datatype == 1:  # int32
                    array = np.frombuffer(data, dtype=np.int32)
                elif datatype == 2:  # int16
                    array = np.frombuffer(data, dtype=np.int16)
                elif datatype == 3:  # uint16
                    array = np.frombuffer(data, dtype=np.uint16)
                else:
                    array = np.frombuffer(data, dtype=np.uint16)
                
                # 重塑数组
                array = array.reshape((ydim, xdim))
                
                # 归一化到0-255范围
                if array.dtype in [np.float32, np.int32, np.int16]:
                    array = array.astype(np.float32)
                    min_val = np.min(array)
                    max_val = np.max(array)
                    if max_val > min_val:
                        array = ((array - min_val) / (max_val - min_val) * 255).astype(np.uint8)
                    else:
                        array = np.zeros_like(array, dtype=np.uint8)
                else:
                    # uint16 直接缩放到 uint8
                    array = (array / 256).astype(np.uint8)
                
                # 转换为PIL Image
                image = Image.fromarray(array, mode='L')
                return image
                
        except Exception as e:
            raise Exception(f"SPE文件读取失败: {str(e)}")
            
    def convert_32bit_tif_to_8bit(self, img):
        """将32bit TIF图像转换为8bit格式"""
        try:
            # 获取图像信息
            self.log_message(f"    检测到32bit图像: 模式={img.mode}, 尺寸={img.size}")
            
            # 转换为numpy数组以便处理
            img_array = np.array(img)
            
            # 获取数据范围
            min_val = np.min(img_array)
            max_val = np.max(img_array)
            self.log_message(f"    数据范围: {min_val:.6f} 到 {max_val:.6f}")
            
            # 检查数据是否全为0或常数
            if max_val == min_val:
                self.log_message(f"    警告: 图像数据为常数 ({min_val})，将创建全黑图像")
                # 创建全黑图像
                img_8bit = Image.new('L', img.size, 0)
            else:
                # 归一化到0-255范围
                # 使用线性拉伸
                img_array_normalized = ((img_array - min_val) / (max_val - min_val) * 255).astype(np.uint8)
                
                # 转换为PIL图像
                img_8bit = Image.fromarray(img_array_normalized, mode='L')
                
                self.log_message(f"    成功转换为8bit灰度图像")
            
            return img_8bit
            
        except Exception as e:
            raise Exception(f"32bit TIF转换失败: {str(e)}")
        
    def start_conversion(self):
        input_path = self.input_path_var.get()
        output_path = self.output_path_var.get()
        output_format = self.format_var.get()
        
        if not input_path or not output_path:
            messagebox.showerror("错误", "请选择输入和输出文件夹")
            return
            
        if not os.path.exists(input_path):
            messagebox.showerror("错误", "输入文件夹不存在")
            return
            
        # 创建输出文件夹
        os.makedirs(output_path, exist_ok=True)
        
        # 在新线程中运行转换
        self.convert_btn.config(state='disabled')
        self.progress_var.set(0)
        self.status_var.set("正在转换...")
        self.log_text.delete(1.0, tk.END)
        
        thread = threading.Thread(target=self.convert_images, args=(input_path, output_path, output_format))
        thread.daemon = True
        thread.start()
        
    def convert_images(self, input_path, output_path, output_format):
        try:
            # 支持的输入格式
            input_extensions = ['*.tif', '*.tiff', '*.spe']
            
            # 查找所有匹配的文件
            all_files = []
            for ext in input_extensions:
                pattern = os.path.join(input_path, ext)
                files = glob.glob(pattern)
                self.log_message(f"查找模式 {pattern}: 找到 {len(files)} 个文件")
                all_files.extend(files)
                # 也查找大写扩展名（避免重复）
                pattern_upper = os.path.join(input_path, ext.upper())
                files_upper = glob.glob(pattern_upper)
                self.log_message(f"查找模式 {pattern_upper}: 找到 {len(files_upper)} 个文件")
                # 只添加不重复的文件
                for file in files_upper:
                    if file not in all_files:
                        all_files.append(file)
                
            if not all_files:
                self.log_message("未找到任何支持的图像文件")
                self.update_status("转换完成 - 未找到文件")
                self.enable_convert_button()
                return
                
            self.log_message(f"找到 {len(all_files)} 个文件")
            
            converted_count = 0
            failed_count = 0
            
            for i, file_path in enumerate(all_files):
                try:
                    filename = os.path.basename(file_path)
                    name_without_ext = os.path.splitext(filename)[0]
                    output_filename = f"{name_without_ext}.{output_format}"
                    output_file_path = os.path.join(output_path, output_filename)
                    
                    self.log_message(f"正在转换: {filename}")
                    
                    # 检查是否为SPE文件
                    file_ext = os.path.splitext(filename)[1].lower()
                    if file_ext == '.spe':
                        # 使用SPE文件读取方法
                        img = self.read_spe_file(file_path)
                    else:
                        # 使用PIL打开其他格式
                        img = Image.open(file_path)
                    
                    # 处理32bit浮点数图像（如TIFF的F模式）
                    if img.mode == 'F':
                        self.log_message(f"    检测到32bit浮点数图像，开始转换...")
                        img = self.convert_32bit_tif_to_8bit(img)
                    
                    # 如果是RGBA模式且输出格式是JPG，转换为RGB
                    if img.mode == 'RGBA' and output_format.lower() == 'jpg':
                        # 创建白色背景
                        background = Image.new('RGB', img.size, (255, 255, 255))
                        background.paste(img, mask=img.split()[-1])  # 使用alpha通道作为mask
                        img = background
                    elif img.mode != 'RGB' and output_format.lower() == 'jpg':
                        img = img.convert('RGB')
                    elif img.mode == 'L' and output_format.lower() == 'jpg':
                        # 灰度图像转换为RGB
                        img = img.convert('RGB')
                        
                    # 保存图像
                    if output_format.lower() == 'jpg':
                        img.save(output_file_path, 'JPEG', quality=95)
                    else:
                        img.save(output_file_path, 'PNG')
                            
                    converted_count += 1
                    self.log_message(f"✓ 成功转换: {filename} -> {output_filename}")
                    
                except Exception as e:
                    failed_count += 1
                    self.log_message(f"✗ 转换失败: {filename} - {str(e)}")
                    self.log_message(f"   文件路径: {file_path}")
                    self.log_message(f"   文件扩展名: {file_ext}")
                    
                # 更新进度
                progress = (i + 1) / len(all_files) * 100
                self.update_progress(progress)
                
            # 转换完成
            self.log_message(f"\n转换完成!")
            self.log_message(f"成功转换: {converted_count} 个文件")
            if failed_count > 0:
                self.log_message(f"转换失败: {failed_count} 个文件")
                
            self.update_status(f"转换完成 - 成功: {converted_count}, 失败: {failed_count}")
            
        except Exception as e:
            self.log_message(f"转换过程中发生错误: {str(e)}")
            self.update_status("转换失败")
            
        finally:
            self.enable_convert_button()

def main():
    root = tk.Tk()
    app = ImageConverter(root)
    root.mainloop()

if __name__ == "__main__":
    main() 